/*
 * =====================================================================================
 *
 *       Filename:  ftp.cpp
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  11.08.2012 00:35:09
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *   Organization:  
 *
 * =====================================================================================
 */
#include "ftp.hpp"

namespace Drosera {

Ftp::Ftp ( SSL *data ) {
	
	setSSL ( data );
	fcntl ( getSocket(), F_SETFL, fcntl ( getSocket(), F_GETFL, 0 ) | O_NONBLOCK );

	tmpnam ( m_tmp );
	m_info.status = false;

	m_io.set< Ftp, &Ftp::checkSocket> ( this );
        m_io.start ( getSocket(), ev::READ );

}

Ftp::~Ftp ( void ) {
	info ( "Ftp-Connection Closed" );
	m_io.stop();

}

void Ftp::checkSocket ( ev::io &watcher, int revents ) {

	int ret = 0;

	if ( EV_ERROR & revents ) {
        	error( "got invalid event" );
        	return;
        }

	std::string buf;
 
        if ( !revents & EV_READ ){
		m_io.set ( ev::READ | ev::WRITE );
		return;
	}

	if ( ( ret = recvSocket ( buf )) != 0 ) {
		warning ( "Problem recieving bytes", ret );
		m_signalDisconnected ( m_id, ERRORDISCON );
		return;
	}

	if ( m_info.status ) {
		ret = getFileInfo ( buf );
		if ( !ret ) return;
		warning ( "Problem getting fileinfos!", ret );
		m_signalDisconnected ( m_id, ERRORINFO );
	}

	ret = writeBuffer ( buf );

	if ( ret == -1 ) {
		warning ( "Problem writing file!" );
		m_signalDisconnected ( m_id, ERRORWRITE );
	} else if ( ret == 0 ) {
		if ( checkFile () != 0 ) {
			m_signalDisconnected ( m_id, ERRORCORRUPT );
			return;
		}
		m_signalDisconnected ( m_id, NOERROR );
	}
	m_signalUpdate ( m_id );

}

int Ftp::getFileInfo ( std::string& buf ) {

	std::stringstream ss ( std::ios::in | std::ios::binary | std::ios::out );
	ss.str ( buf );

	Drosera::FileInfo info;

	if ( !info.ParseFromIstream ( &ss ) )
		return -1;

	m_info.fsize = info.fsize();
	m_info.md5 = std::move ( info.md5() );
	m_info.name = std::move ( info.name() );
	m_info.status = true;

	return 0;

}

int Ftp::checkFile ( void ) {

	struct stat filestatus;
	stat ( m_tmp, &filestatus );
	
	if ( filestatus.st_size != m_info.fsize ) return -1;
	if ( !checkMD5 ( m_tmp, m_info.md5 ) ) return -2;

	return 0;
}

int Ftp::writeBuffer ( std::string& buf ) {
	
	std::ofstream io ( m_tmp, std::ios::out | std::ios::binary );

	io.write ( buf.c_str(), buf.size() );

	if ( !io.good() ) return -1;

	if ( buf.size() != MAXRECV ) return 0;

	return 1;
}

bool Ftp::checkMD5 ( std::string& path, std::string& hash ) {
	return checkMD5 ( ( char* ) path.c_str(), hash );
}

bool Ftp::checkMD5 ( char *path, std::string& hash ) {

	std::string ret = getMD5 ( path );

	if ( ret == hash ) return true;

	return false;

}

unsigned int Ftp::getFtpId ( void ) {
	return m_id;
}


int Ftp::sendFile ( std::string& path, std::string& where ) {
	return sendFile ( ( char* ) path.c_str(), ( char* ) where.c_str() );
}

int Ftp::sendFile ( char *path, char *where ) {

	if ( fileExist ( path ) ) return -1;

	struct stat filestatus;
	Drosera::FileInfo info;
	
	stat ( path, &filestatus );
	info.set_fsize ( filestatus.st_size );
	info.set_md5 ( getMD5 ( path ) );
	info.set_name ( where );

	std::stringstream ss ( std::ios::in | std::ios::out | std::ios::binary );

	if ( !info.SerializeToOstream ( &ss ) )
		return -2;
	
	std::string buf = ss.str();
	if ( !sendSocket ( buf ) )
		return -3;
	
	std::fstream pfile ( path, std::ios::in | std::ios::binary );

	char part[MAXSEND];

	while ( !pfile.good() ) {
		pfile.read ( part, MAXSEND );
		buf.clear();
		buf.append ( part, pfile.gcount() );
		if ( sendSocket ( buf ) )  return -4;
	}
	return 0;
}

Signal<void, unsigned int>& Ftp::signalUpdate ( void ) {
	return m_signalUpdate;
}

Signal<void, unsigned int, int>& Ftp::signalDisconnected ( void ) {
	return m_signalDisconnected;
}

}
